home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / DTS.Lib / File2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-09  |  32.6 KB  |  1,114 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:         DTS.Lib
  5. ** File:            file2.c
  6. ** Some code from:  Traffic Light 2.0 version, by Keith Rollin & John Harvey
  7. ** Modified by:     Eric Soldan
  8. **
  9. ** Copyright © 1990-1993 Apple Computer, Inc.
  10. ** All rights reserved.
  11. */
  12.  
  13. /* You may incorporate this sample code into your applications without
  14. ** restriction, though the sample code has been provided "AS IS" and the
  15. ** responsibility for its operation is 100% yours.  However, what you are
  16. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  17. ** after having made changes. If you're going to re-distribute the source,
  18. ** we require that you make it clear in the source that the code was
  19. ** descended from Apple Sample Code, but that you've made changes. */
  20.  
  21.  
  22.  
  23. /*****************************************************************************/
  24.  
  25.  
  26.  
  27. #include "DTS.Lib2.h"
  28. #include "DTS.Lib.protos.h"
  29.  
  30. #ifndef __ERRORS__
  31. #include <Errors.h>
  32. #endif
  33.  
  34. #ifndef __FILES__
  35. #include <Files.h>
  36. #endif
  37.  
  38. #if __MOVIESUPPORT__
  39.  
  40. #ifndef __IMAGECOMPRESSION__
  41. #include <ImageCompression.h>
  42. #endif
  43.  
  44. #ifndef __MOVIES__
  45. #include <Movies.h>
  46. #endif
  47.  
  48. #endif
  49.  
  50. #ifndef __PACKAGES__
  51. #include <Packages.h>
  52. #endif
  53.  
  54. #ifndef __RESOURCES__
  55. #include <Resources.h>
  56. #endif
  57.  
  58. #ifndef __TOOLUTILS__
  59. #include <ToolUtils.h>
  60. #endif
  61.  
  62.  
  63.  
  64. /*****************************************************************************/
  65.  
  66.  
  67.  
  68. extern OSType        gDocCreator;    /* Initialized to gSignature by Init. */
  69. extern long            gAppWindowAttr;    /* Initialized by app in Window.c. */
  70. extern long            gQTVersion;        /* Initialized by Utilities.c in InitQuickTime. */
  71.  
  72. extern TreeObjHndl    gWindowFormats;    /* If non-nil, then get descriptions of windows from here. */
  73.  
  74. extern short        gTypeListLen;    /* Initialized by app in File.c. */
  75. extern OSType        *gTypeListPtr;    /* Initialized by app in File.c. */
  76.  
  77. FileFilterUPP        gSFGetFileFilterUPP;
  78.  
  79. static OSErr        Create_OpenFile(FSSpec *file, short *refNum, OSType sftype);
  80.  
  81.  
  82.  
  83. /*****************************************************************************/
  84. /*****************************************************************************/
  85.  
  86.  
  87.  
  88. /* This function disposes of the document.  It checks to see if a file is
  89. ** currently open for the document.  If it is, then the document is closed.
  90. ** Once there is no open file for the document, the memory occupied by the
  91. ** document is released. */
  92.  
  93. #pragma segment File
  94. OSErr    DisposeDocument(FileRecHndl frHndl)
  95. {
  96.     OSErr        err, err2;
  97.     short        refNum;
  98.     WindowPtr    window;
  99.     Movie        movie;
  100.  
  101.     err = noErr;
  102.  
  103.     if (frHndl) {
  104.  
  105.         if ((refNum = (*frHndl)->fileState.refNum) != kInvalRefNum) {        /* If file open... */
  106.  
  107.             if ((*frHndl)->fileState.sfType == MovieFileType)        /* If movie file... */
  108.                 err = CloseMovieFile(refNum);                        /* Close it. */
  109.  
  110.             else                                                    /* If not movie file... */
  111.                 err = FSClose(refNum);                                /* Close it.       */
  112.         }
  113.  
  114.         CloseDocResFile(frHndl);                    /* Close resource fork, if opened. */
  115.  
  116.         window = (*frHndl)->fileState.window;
  117.         err2    = DoFreeDocument(frHndl);            /* Free all application-specific document ram. */
  118.  
  119.         movie = (*frHndl)->fileState.movie;
  120.         if (movie)
  121.             DisposeMovie(movie);                    /* If we have a movie, dispose it. */
  122.  
  123.         if (window)
  124.             SetWRefCon(window, (long)nil);            /* Mark window as no longer having a document. */
  125.  
  126.         if (!err)
  127.             err = err2;
  128.  
  129.         DisposeHandle((Handle)frHndl);                /* Release memory for the document handle. */
  130.     }
  131.  
  132.     return(err);
  133. }
  134.  
  135.  
  136.  
  137. /*****************************************************************************/
  138.  
  139.  
  140.  
  141. /* This function creates a new document.  A handle is created as the
  142. ** reference to the document.  Header information is placed in this handle.
  143. ** The application-specific data follows this header information.  The
  144. ** handle is returned (or nil upon failure), and typically the handle is
  145. ** then stored in the refCon field of the window.  Note that this is a
  146. ** convention, and is not mandatory.  This allows a document to exist that
  147. ** has no window.  A document with no window is useful when the application
  148. ** is called from the finder in response to a print request.  The document
  149. ** can be loaded and printed without involving a window on the screen. */
  150.  
  151. #pragma segment File
  152. OSErr    NewDocument(FileRecHndl *returnHndl, OSType sftype, Boolean incTitleNum)
  153. {
  154.     long                size;
  155.     FileRecHndl            frHndl;
  156.     FileRecPtr            frPtr;
  157.     Str255                untitled;
  158.     StringPtr            pstr;
  159.     OSErr                err;
  160.     short                i;
  161.     Movie                movie;
  162.     TreeObjHndl            wobj;
  163.     PositionWndProcPtr    windowPlacementProc;
  164.     static short        untitledCount;
  165.  
  166.     if (!sftype) {
  167.         if (returnHndl == (FileRecHndl *)-1)
  168.             --untitledCount;
  169.         if (!returnHndl)
  170.             untitledCount = 0;
  171.         return(noErr);
  172.     }
  173.  
  174.     if (returnHndl)
  175.         *returnHndl = nil;
  176.  
  177.     err  = memFullErr;                /* Assume that we will fail. */
  178.  
  179.     size = InitDocumentSize(sftype);
  180.         /* Call the application and ask it how big the frHndl should be for
  181.         ** this document type.  We can't know, so we'll ask. */
  182.  
  183.     frHndl = (FileRecHndl)NewHandleClear(size);
  184.     if (frHndl) {
  185.         /* Create (or try to) the frHndl, initialized to all 0's */
  186.  
  187.         if (returnHndl)
  188.             *returnHndl = frHndl;
  189.  
  190.         windowPlacementProc = StaggerWindow;
  191.         wobj = nil;
  192.         if (gWindowFormats) {
  193.             for (i = 0; i < (*gWindowFormats)->numChildren; ++i) {
  194.                 wobj = GetChildHndl(gWindowFormats, i);
  195.                 if (sftype == mDerefWFMT(wobj)->sfType) {
  196.                     windowPlacementProc = nil;
  197.                         /* If using 'WFMT' descriptions, this will be fully determined later. */
  198.                     pcpy(untitled, (StringPtr)mDerefWFMT(wobj)->title);
  199.                     if (untitled[0])
  200.                         if (untitled[1] == ' ')
  201.                             untitled[0] = 0;
  202.                                 /* If title begins with a space, it's just a comment. */
  203.  
  204.                     break;
  205.                 }
  206.                 wobj = nil;
  207.             }
  208.         }
  209.         if (!wobj) {
  210.             for (i = gTypeListLen; --i;) if (sftype == gTypeListPtr[i]) break;
  211.                 /* Walk the typeList to find this file type.  We are interested in
  212.                 ** where we find the entry.  The position we find it is used as a
  213.                 ** string number into the rDefaultTitles STR# resource.  We get
  214.                 ** an individual string from this location.  This allows us to
  215.                 ** have different default titles for different document types. */
  216.     
  217.             for (++i; i; i--) {
  218.                 GetIndString(untitled, rDefaultTitles, i);
  219.                 if (untitled[0]) break;        /* Quit if we succeeded at getting one. */
  220.             }
  221.         }
  222.  
  223.         (*frHndl)->fileState.modNum = GetModNum();
  224.             /* In case GetModNum gets moved to another code segment, set this value
  225.             ** prior to dereferencing frHndl into frPtr. */
  226.  
  227.         frPtr = *frHndl;
  228.         frPtr->fileState.sfType                  = sftype;
  229.         frPtr->fileState.modTick                 = TickCount();
  230.         frPtr->fileState.refNum                  = kInvalRefNum;
  231.         frPtr->fileState.resRefNum               = kInvalRefNum;
  232.         frPtr->fileState.fss.vRefNum             = kInvalVRefNum;
  233.         frPtr->fileState.windowID                = rWindow;
  234.             /* The above sets the fileState constants for the document.  Note
  235.             ** that we use a default 'WIND' ID for the expected window resource.
  236.             ** This can be changed later, if the default isn't good enough. */
  237.  
  238.         if (wobj) {
  239.             frPtr->fileState.windowID      = mDerefWFMT(wobj)->windowID;
  240.             frPtr->fileState.attributes    = mDerefWFMT(wobj)->attributes;
  241.             frPtr->fileState.hScrollIndent = mDerefWFMT(wobj)->hScrollIndent;
  242.             frPtr->fileState.vScrollIndent = mDerefWFMT(wobj)->vScrollIndent;
  243.             frPtr->fileState.leftSidebar   = mDerefWFMT(wobj)->leftSidebar;
  244.             frPtr->fileState.topSidebar    = mDerefWFMT(wobj)->topSidebar;
  245.                 /* Set window attributes as described in resource. */
  246.         }
  247.         else
  248.             frPtr->fileState.attributes = gAppWindowAttr;
  249.                 /* Set window attributes for the main document type. If the document
  250.                 ** is not the main type, then the application's InitDocument function
  251.                 ** will have to change it. */
  252.  
  253.         frPtr->fileState.getDocWindow        = windowPlacementProc;
  254.         frPtr->fileState.adjustMenuItemsProc = AdjustMenuItems;
  255.         frPtr->fileState.doMenuItemProc      = DoMenuItem;
  256.         switch (frPtr->fileState.attributes & (kwIsPalette | kwIsModalDialog)) {
  257.             case kwIsPalette:
  258.                 frPtr->fileState.calcFrameRgnProc  = PaletteCalcFrameRgn;
  259.                 frPtr->fileState.contentClickProc  = PaletteContentClick;
  260.                 frPtr->fileState.contentKeyProc    = PaletteContentKey;
  261.                 frPtr->fileState.drawFrameProc     = PaletteDrawFrame;
  262.                 frPtr->fileState.freeDocumentProc  = PaletteFreeDocument;
  263.                 frPtr->fileState.freeWindowProc    = PaletteFreeWindow;
  264.                 frPtr->fileState.imageProc         = PaletteImageDocument;
  265.                 frPtr->fileState.initContentProc   = PaletteInitContent;
  266.                 frPtr->fileState.readDocumentProc  = nil;
  267.                 frPtr->fileState.resizeContentProc = PaletteResizeContent;
  268.                 frPtr->fileState.scrollFrameProc   = PaletteScrollFrame;
  269.                 frPtr->fileState.undoFixupProc     = PaletteUndoFixup;
  270.                 frPtr->fileState.windowCursorProc  = PaletteWindowCursor;
  271.                 frPtr->fileState.writeDocumentProc = nil;
  272.                 break;
  273.             case kwIsModalDialog:
  274.                 frPtr->fileState.calcFrameRgnProc    = DialogCalcFrameRgn;
  275.                 frPtr->fileState.contentClickProc    = DialogContentClick;
  276.                 frPtr->fileState.contentKeyProc      = DialogContentKey;
  277.                 frPtr->fileState.drawFrameProc       = DialogDrawFrame;
  278.                 frPtr->fileState.freeDocumentProc    = DialogFreeDocument;
  279.                 frPtr->fileState.freeWindowProc      = DialogFreeWindow;
  280.                 frPtr->fileState.imageProc           = DialogImageDocument;
  281.                 frPtr->fileState.initContentProc     = DialogInitContent;
  282.                 frPtr->fileState.readDocumentProc    = nil;
  283.                 frPtr->fileState.resizeContentProc   = DialogResizeContent;
  284.                 frPtr->fileState.scrollFrameProc     = DialogScrollFrame;
  285.                 frPtr->fileState.undoFixupProc       = DialogUndoFixup;
  286.                 frPtr->fileState.windowCursorProc    = DialogWindowCursor;
  287.                 frPtr->fileState.writeDocumentProc   = nil;
  288.                 frPtr->fileState.adjustMenuItemsProc = DialogAdjustMenuItems;
  289.                 frPtr->fileState.doMenuItemProc      = DialogDoMenuItem;
  290.                 break;
  291.             default:
  292.                 frPtr->fileState.calcFrameRgnProc  = CalcFrameRgn;
  293.                 frPtr->fileState.contentClickProc  = ContentClick;
  294.                 frPtr->fileState.contentKeyProc    = ContentKey;
  295.                 frPtr->fileState.drawFrameProc     = DrawFrame;
  296.                 frPtr->fileState.freeDocumentProc  = FreeDocument;
  297.                 frPtr->fileState.freeWindowProc    = FreeWindow;
  298.                 frPtr->fileState.imageProc         = ImageDocument;
  299.                 frPtr->fileState.initContentProc   = InitContent;
  300.                 frPtr->fileState.readDocumentProc  = ReadDocument;
  301.                 frPtr->fileState.resizeContentProc = ResizeContent;
  302.                 frPtr->fileState.scrollFrameProc   = ScrollFrame;
  303.                 frPtr->fileState.undoFixupProc     = UndoFixup;
  304.                 frPtr->fileState.windowCursorProc  = WindowCursor;
  305.                 frPtr->fileState.writeDocumentProc = WriteDocument;
  306.                 break;
  307.         }
  308.  
  309.         frPtr->fileState.windowSizeBounds.left   = kMinWindowWidth;
  310.         frPtr->fileState.windowSizeBounds.top    = kMinWindowHeight;
  311.         frPtr->fileState.windowSizeBounds.right  = kMaxWindowWidth;
  312.         frPtr->fileState.windowSizeBounds.bottom = kMaxWindowHeight;
  313.             /* Default min/max window size for growIcon. */
  314.  
  315.         pstr = frPtr->fileState.fss.name;
  316.         pcpy(pstr, untitled);
  317.         if (pstr[0]) {
  318.             if (incTitleNum)
  319.                 ++untitledCount;
  320.             pcatdec(pstr, untitledCount);
  321.                 /* Create the default document title.  It is stored in the FSSpec,
  322.                 ** as we can't place it in the window title.  We don't have a window
  323.                 ** yet to "title".  This will happen later.  The title is gotten from
  324.                 ** the FSSpec, so we are effectively done. */
  325.         }
  326.  
  327.         err = InitDocument(frHndl);
  328.             /* Call the application for any additional document initialization.
  329.             ** Other handles may need to be allocated.  The default values above
  330.             ** may be incorrect for a certain document type.  This gives the
  331.             ** application a chance to change any defaults that are incorrect. */
  332.  
  333.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  334.             InitQuickTime();
  335.             if (!gQTVersion) return(paramErr);
  336.             movie = NewMovie(newMovieActive);
  337.             err   = GetMoviesError();
  338.             if (!err) {
  339.                 ClearMovieChanged(movie);
  340.                 (*frHndl)->fileState.movie = movie;
  341.             }
  342.         }
  343.  
  344.         if (err) {
  345.             DisposeHandle((Handle)frHndl);
  346.             if (returnHndl)
  347.                 *returnHndl = nil;
  348.                     /* If the application couldn't complete the document
  349.                     ** initialization, pitch the handle. */
  350.         }
  351.     }
  352.  
  353.     return(err);
  354. }
  355.  
  356.  
  357.  
  358. /*****************************************************************************/
  359.  
  360.  
  361.  
  362. #pragma segment File
  363. OSErr    OpenDocument(FileRecHndl *result, FSSpecPtr fileToOpen, char permission)
  364. {
  365.     StandardFileReply    reply;
  366.     short                refNum;
  367.     FileRecHndl            frHndl, ff;
  368.     WindowPtr            ww;
  369.     OSErr                err;
  370.     FSSpec                myFileSpec, fss;
  371.     DialogPtr            openDialog;
  372.     short                item;
  373.     FInfo                finderInfo;
  374.     Boolean                openMovie;
  375.     Movie                movie;
  376.     static SFTypeList    typeList = {MovieFileType};
  377.  
  378.     *result = nil;        /* Assume we will fail. */
  379.  
  380.     openMovie = false;
  381.     if (fileToOpen == kOpenMovie) {
  382.         if (!gQTVersion) return(paramErr);        /* Can't do movies without QuickTime. */
  383.         fileToOpen = nil;
  384.         openMovie  = true;
  385.     }
  386.  
  387.     if (!fileToOpen) {
  388.         if (openMovie) {
  389.             StandardGetFilePreview(0L, 1, typeList, &reply);
  390.             if (reply.sfGood)
  391.                 myFileSpec = reply.sfFile;
  392.             else
  393.                 return(userCanceledErr);    /* User canceled. */
  394.         }
  395.         else {
  396.             if (DisplayGetFile(&reply, gTypeListLen, gTypeListPtr))    /* Let user pick file. */
  397.                 myFileSpec = reply.sfFile;                            /* User's choice.       */
  398.             else
  399.                 return(userCanceledErr);    /* User canceled. */
  400.         }
  401.     }
  402.     else {
  403.         err = HGetFInfo(fileToOpen->vRefNum, fileToOpen->parID, fileToOpen->name, &finderInfo);
  404.         if (err) return(err);
  405.         reply.sfType = finderInfo.fdType;        /* OSType of file. */
  406.         myFileSpec   = *fileToOpen;                /* Pre-designated file to open. */
  407.     }
  408.  
  409.     err = NewDocument(&frHndl, reply.sfType, false);
  410.     if (err) return(err);
  411.         /* We couldn't create an empty document, so give it up. */
  412.  
  413.     err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  414.     if (err == paramErr)
  415.         err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  416.  
  417.     if (err == permErr) {
  418.         permission = fsRdPerm;
  419.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  420.         if (err == paramErr)
  421.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  422.         if (!err)
  423.             (*frHndl)->fileState.readOnly = true;
  424.     }
  425.  
  426.     if (err == opWrErr) {
  427.  
  428.         for (ww = nil; ((ww = GetNextWindow(ww, 0)) != nil);) {
  429.             ff  = (FileRecHndl)GetWRefCon(ww);
  430.             fss = (*ff)->fileState.fss;
  431.             if (
  432.                 (myFileSpec.vRefNum == fss.vRefNum) &&
  433.                 (myFileSpec.parID   == fss.parID) &&
  434.                 (!pcmp(myFileSpec.name, fss.name))
  435.             ) {
  436.                 *result = ff;
  437.                 return(noErr);
  438.             }
  439.         }
  440.  
  441.         ParamText(myFileSpec.name, nil, nil, nil);
  442.         openDialog = GetCenteredDialog(rOpenReadOnly, nil, nil, (WindowPtr)-1L);
  443.         if (!openDialog) {
  444.             DisposeDocument(frHndl);
  445.             return(err);
  446.         }
  447.  
  448.         OutlineDialogItem(openDialog, kOpenYes);
  449.         DoSetCursor(&qd.arrow);
  450.         UnhiliteWindows();
  451.         ModalDialog(gKeyEquivFilterUPP, &item);
  452.         DisposeDialog(openDialog);
  453.         HiliteWindows();
  454.         if (item != kOpenYes) {
  455.             DisposeDocument(frHndl);
  456.             return(userCanceledErr);
  457.         }
  458.  
  459.         (*frHndl)->fileState.readOnly = true;
  460.         permission = fsRdPerm;
  461.         err = HOpenDF(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  462.         if (err == paramErr)
  463.             err = HOpen(myFileSpec.vRefNum, myFileSpec.parID, myFileSpec.name, permission, &refNum);
  464.     }
  465.  
  466.     if (err) {
  467.         DisposeDocument(frHndl);
  468.         return(err);
  469.     }
  470.  
  471.     if ((*frHndl)->fileState.sfType == MovieFileType) {
  472.         FSClose(refNum);                            /* Close file, as it wasn't opened as movie. */
  473.         movie = (*frHndl)->fileState.movie;
  474.         if (movie) {
  475.             DisposeMovie(movie);                    /* NewDocument created an empty movie, */
  476.             (*frHndl)->fileState.movie = nil;        /* so first get rid of it. */
  477.         }
  478.         err = OpenMovieFile(&myFileSpec, &refNum, permission);
  479.         if (err) {
  480.             DisposeDocument(frHndl);
  481.             return(err);
  482.         }
  483.     }
  484.  
  485.     (*frHndl)->fileState.fss    = myFileSpec;
  486.     (*frHndl)->fileState.refNum = refNum;
  487.  
  488.     err = DoReadDocument(frHndl);
  489.     if (err) {
  490.         DisposeDocument(frHndl);
  491.         return(err);
  492.     }
  493.  
  494.     *result = frHndl;
  495.     return(noErr);
  496. }
  497.  
  498.  
  499.  
  500. /*****************************************************************************/
  501.  
  502.  
  503.  
  504. #pragma segment File
  505. OSErr    SaveDocument(FileRecHndl frHndl, WindowPtr window, short saveMode)
  506. {
  507.     Str255                closeOrQuit;
  508.     short                item, refNum, resID, resWasOpen;
  509.     long                createFlags;
  510.     StandardFileReply    reply;
  511.     OSErr                err;
  512.     Movie                movie;
  513.     Boolean                doPrompt;
  514.     DialogPtr            saveDialog;
  515.  
  516.     err = noErr;
  517.  
  518. /*    When entering, saveMode is set to the menu command number of the
  519. **    the item that prompted this. Current settings are kSave, kSaveAs,
  520. **    kClose, and kQuit. */
  521.  
  522.     if (saveMode != kSaveAs) {                            /* If not save as...                  */
  523.         if (!(*frHndl)->fileState.docDirty) {            /* If file clean...                      */
  524.             if ((*frHndl)->fileState.refNum)            /* If document has a file...          */
  525.                 if (!(*frHndl)->fileState.readOnly)        /* If we are allowed to touch file... */
  526.                     if ((*frHndl)->fileState.attributes & kwOpenAtOldLoc)
  527.                         DoWriteDocumentHeader(frHndl);
  528.                             /* Write out document location and print record information. */
  529.                             /* Ignore errors, as saving the location is a bonus. */
  530.             return(noErr);                                /* Consider it saved. */
  531.         }
  532.     }
  533.  
  534.     pcpy(reply.sfFile.name, (*frHndl)->fileState.fss.name);
  535.  
  536.     if ((saveMode == kClose) || (saveMode == kQuit)) {
  537.         /* If implicit save... */
  538.  
  539.         GetIndString(closeOrQuit, rFileIOStrings,
  540.                      (saveMode == kClose) ? sWClosing : sQuitting);
  541.         ParamText(reply.sfFile.name, closeOrQuit, nil, nil);
  542.  
  543.         saveDialog = GetCenteredDialog(rYesNoCancel, nil, window, (WindowPtr)-1L);
  544.  
  545.         if (saveDialog) {
  546.             OutlineDialogItem(saveDialog, kSaveYes);
  547.             DoSetCursor(&qd.arrow);
  548.             UnhiliteWindows();
  549.             ModalDialog(gKeyEquivFilterUPP, &item);
  550.             DisposeDialog(saveDialog);
  551.             HiliteWindows();
  552.         }
  553.         else
  554.             item = kSaveNo;
  555.                 /* If the dialog isn't displayed, then AppleScript doesn't want it to.
  556.                 ** In this case, we were probably AppleScripted the whole time, so
  557.                 ** the document is an AppleScript-produced document.  The script is
  558.                 ** done with the document, so ditch the document. */
  559.  
  560.         if (item != kSaveYes) {
  561.             err = noErr;
  562.             if (item == kSaveCanceled)
  563.                 err = userCanceledErr;
  564.             return(err);
  565.         }
  566.     }
  567.  
  568.     doPrompt = (
  569.         (saveMode == kSaveAs) ||
  570.         ((*frHndl)->fileState.refNum == kInvalRefNum)
  571.     );
  572.  
  573.     if (doPrompt) {
  574.         /* Prompt with SFGetFile if doing a Save As or have never saved before. */
  575.  
  576.         if (!DisplayPutFile(&reply)) return(userCanceledErr);    /* User canceled the save. */
  577.  
  578.         resWasOpen = (*frHndl)->fileState.resRefNum;
  579.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  580.             if ((*frHndl)->fileState.refNum != kInvalRefNum) {
  581.                 CloseDocResFile(frHndl);        /* Close resource fork, if opened. */
  582.                 FSClose((*frHndl)->fileState.refNum);
  583.             }            /* Close the old file.  Don't respond to any error here because
  584.                         ** the user may be trying to do a save-as because their old file
  585.                         ** is bad.  If we fail to close the old file, and then respond
  586.                         ** to the error, the user won't get the opportunity to save
  587.                         ** their document to a new file. */
  588.  
  589.             (*frHndl)->fileState.refNum      = kInvalRefNum;
  590.             (*frHndl)->fileState.fss.vRefNum = kInvalVRefNum;
  591.             err = Create_OpenFile(&reply.sfFile, &refNum, (*frHndl)->fileState.sfType);
  592.             if (err) return(err);
  593.         }
  594.         else {
  595.             createFlags = createMovieFileDeleteCurFile;
  596.             err = CreateMovieFile(&reply.sfFile, gDocCreator, 0, createFlags, &refNum, nil);
  597.             if (err) return(err);
  598.             resID = 0;
  599.             err = AddMovieResource((*frHndl)->fileState.movie, refNum, &resID, nil);
  600.             if (err) return(err);
  601.             (*frHndl)->fileState.movieResID = resID;
  602.         }
  603.  
  604.         (*frHndl)->fileState.fss    = reply.sfFile;        /* This is the new file. */
  605.         (*frHndl)->fileState.refNum = refNum;
  606.  
  607.         if (resWasOpen) {
  608.             UseDocResFile(frHndl, &resWasOpen, fsRdWrPerm);
  609.             UseResFile(resWasOpen);
  610.         }
  611.  
  612.         if (window)
  613.             NewWindowTitle(window, nil);
  614.     }
  615.     else {
  616.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  617.             movie  = (*frHndl)->fileState.movie;
  618.             refNum = (*frHndl)->fileState.refNum;
  619.             resID  = (*frHndl)->fileState.movieResID;
  620.             err = UpdateMovieResource(movie, refNum, resID, nil);
  621.             if (err) return(err);
  622.         }
  623.     }
  624.  
  625.     err = DoWriteDocument(frHndl);
  626.     if (err) return(err);
  627.  
  628.     (*frHndl)->fileState.docDirty = false;
  629.     (*frHndl)->fileState.readOnly = false;
  630.     return(noErr);
  631. }
  632.  
  633.  
  634.  
  635. /*****************************************************************************/
  636.  
  637.  
  638.  
  639. /* ConvertOldToNewSFReply
  640. **
  641. ** struct StandardFileReply {            struct SFReply {
  642. **     Boolean     sfGood;                <-    Boolean good;
  643. **     Boolean     sfReplacing;        <-    Boolean copy;
  644. **     OSType         sfType;                <-    OSType fType;
  645. **     FSSpec        sfFile;
  646. **                     vRefNum;        <-    real vRefnum from (short vRefNum)
  647. **                     parID;            <-    real dirID from (short vRefNum)
  648. **                     name;            <-    Str63 fName;
  649. **     ScriptCode    sfScript;            <-    iuSystemScript
  650. **     short         sfFlags;            <-    0
  651. **     Boolean     sfIsFolder;            <-    false
  652. **     Boolean     sfIsVolume;            <-    false
  653. **     long        sfReserved1;        <-    0
  654. **     short        sfReserved2;        <-    0
  655. ** };                                    };
  656. */
  657.  
  658. #pragma segment File
  659. void    ConvertOldToNewSFReply(SFReply *oldReply, StandardFileReply *newReply)
  660. {
  661.     OSErr        err;
  662.     long        ignoredProcID;
  663.  
  664.     newReply->sfGood        = oldReply->good;
  665.     newReply->sfReplacing    = oldReply->copy;        /* Correct assignment? */
  666.     newReply->sfType        = oldReply->fType;
  667.  
  668.     err = GetWDInfo(oldReply->vRefNum,
  669.                     &newReply->sfFile.vRefNum,
  670.                     &newReply->sfFile.parID,
  671.                     &ignoredProcID);
  672.     pcpy(newReply->sfFile.name, oldReply->fName);
  673.  
  674.     /* Punt on the rest... */
  675.     newReply->sfScript        = iuSystemScript;
  676.     newReply->sfFlags        = 0;
  677.     newReply->sfIsFolder    = false;
  678.     newReply->sfIsVolume    = false;
  679.     newReply->sfReserved1    = 0;
  680.     newReply->sfReserved2    = 0;
  681. }
  682.  
  683.  
  684.  
  685. /*****************************************************************************/
  686.  
  687.  
  688.  
  689. /* Opens the file specified by the passed FSSpec, creating it if it doesn't
  690. ** already exist. Returns the refnum of the open file to the application.
  691. ** File Manager errors are reported and returned. */
  692.  
  693. #pragma segment File
  694. static OSErr    Create_OpenFile(FSSpec *file, short *refNum, OSType sftype)
  695. {
  696.     OSErr    err;
  697.  
  698.     err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  699.     if (err == dupFNErr) {
  700.  
  701.         /* The user already told Standard File to replace the old file
  702.            so let's get rid of it. */
  703.  
  704.         HDelete(file->vRefNum, file->parID, file->name);
  705.  
  706.         /* Try creating it again. */
  707.         err = HCreate(file->vRefNum, file->parID, file->name, gDocCreator, sftype);
  708.     }
  709.  
  710.     if (!err) {
  711.         err = HOpenDF(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  712.         if (err == paramErr)
  713.             err = HOpen(file->vRefNum, file->parID, file->name, fsRdWrPerm, refNum);
  714.         if (err)
  715.             HDelete(file->vRefNum, file->parID, file->name);
  716.     }
  717.  
  718.     return(err);
  719. }
  720.  
  721.  
  722.  
  723. /*****************************************************************************/
  724.  
  725.  
  726.  
  727. /* Simple routine to display a list of files with our file type. */
  728.  
  729. #pragma segment File
  730. Boolean    DisplayGetFile(StandardFileReply *reply, short typeListLen, SFTypeList typeList)
  731. {
  732.     Point        where = {100, 100};
  733.     SFReply        oldReply;
  734.  
  735.     if (gSystemVersion >= 0x0700)        /* If new standard file available... */
  736.         StandardGetFile(gSFGetFileFilterUPP, typeListLen, typeList, reply);
  737.  
  738.     else {
  739.         SFGetFile(where, "\pSelect a document to open.",
  740.                          gSFGetFileFilterUPP, typeListLen, typeList, nil, &oldReply);
  741.         ConvertOldToNewSFReply(&oldReply, reply);
  742.     }
  743.  
  744.     return(reply->sfGood);
  745. }
  746.  
  747.  
  748.  
  749. /*****************************************************************************/
  750.  
  751.  
  752.  
  753. /* Displays the StandardFile PutFile dialog box. Fills out the passed reply
  754. ** record, and returns the sfGood field as a result. */
  755.  
  756. #pragma segment File
  757. Boolean    DisplayPutFile(StandardFileReply *reply)
  758. {
  759.     Str255        prompt;
  760.     Point        where = {100, 100};
  761.     SFReply        oldReply;
  762.  
  763.     GetIndString(prompt, rFileIOStrings, sSFprompt);
  764.  
  765.     if (gSystemVersion >= 0x0700)    /* If new standard file available... */
  766.         StandardPutFile(prompt, reply->sfFile.name, reply);
  767.     else {
  768.         SFPutFile(where, prompt, reply->sfFile.name, nil, &oldReply);
  769.         ConvertOldToNewSFReply(&oldReply, reply);
  770.     }
  771.  
  772.     return(reply->sfGood);
  773. }
  774.  
  775.  
  776.  
  777. /*****************************************************************************/
  778.  
  779.  
  780.  
  781. /* Use the resource fork for the designated document file.  This function
  782. ** also returns the old CurResFile, so you can set it back when you are done.
  783. ** Simply call this function, whether or not you have a resource fork.  If
  784. ** there isn't a resource fork, then one will be created.  If there is one,
  785. ** but it isn't open yet, it will be opened.  If it is already opened, it
  786. ** sets it as the current resource fork.  What more do you want? */
  787.  
  788. #define fcbFlgRBit 0x200
  789.  
  790. #pragma segment File
  791. OSErr    UseDocResFile(FileRecHndl frHndl, short *oldRes, char perm)
  792. {
  793.     OSErr        err;
  794.     FSSpec        fss;
  795.     short        res, vrn;
  796.     long        pid;
  797.     FCBPBRec    pb;
  798.     CInfoPBRec    resOpen;
  799.  
  800.     if (oldRes)
  801.         *oldRes = CurResFile();
  802.  
  803.     if ((res = (*frHndl)->fileState.resRefNum) != kInvalRefNum) {
  804.         UseResFile(res);        /* If the resource fork already open, use it. */
  805.         return(ResError());
  806.     }
  807.  
  808.     SetMem(&pb, 0, sizeof(FCBPBRec));            /* Make most of the param block happy. */
  809.     pb.ioRefNum = res = (*frHndl)->fileState.refNum;
  810.     err = PBGetFCBInfoSync(&pb);
  811.     if (err) return(err);
  812.     if (pb.ioFCBFlags & fcbFlgRBit) {
  813.         (*frHndl)->fileState.resRefNum = res;
  814.         UseResFile(res);
  815.         return(ResError());
  816.     }
  817.  
  818.     fss = (*frHndl)->fileState.fss;
  819.     vrn = fss.vRefNum;
  820.     pid = fss.parID;
  821.  
  822.     SetMem(&resOpen, 0, sizeof(CInfoPBRec));    /* Make most of the param block happy. */
  823.     resOpen.hFileInfo.ioVRefNum = vrn;
  824.     resOpen.hFileInfo.ioDirID   = pid;
  825.     resOpen.hFileInfo.ioNamePtr = fss.name;
  826.     err = PBGetCatInfoSync(&resOpen);
  827.     if (err) return(err);
  828.     if (resOpen.hFileInfo.ioFlAttrib & 0x04) {
  829.         /* The 0x04 is to look at the bit that says whether or not the resource fork is
  830.         ** already open.  Why did we do this?  To keep from re-opening a resource fork.
  831.         ** Reopening it actually works, but things get a bit ugly when it is closed.  The
  832.         ** one-and-only reference to the open resource fork would get closed if we thought
  833.         ** we opened it.  This keeps us from doing way-bad things later. */
  834.         UseResFile(resOpen.hFileInfo.ioFRefNum);
  835.         return(ResError());
  836.     }
  837.  
  838.     if ((*frHndl)->fileState.readOnly)
  839.         if (perm  & fsWrPerm) perm ^= fsWrPerm;
  840.  
  841.     SetResLoad(false);
  842.     res = HOpenResFile(vrn, pid, fss.name, perm);
  843.     err = ResError();
  844.     SetResLoad(true);
  845.         /* Try opening the resource fork. */
  846.  
  847.     if (err) {
  848.         if (err != eofErr) return(err);                    /* Some errors we can't handle here. */
  849.         HCreateResFile(vrn, pid, fss.name);                /* No resource fork, so create one.  */
  850.         err = ResError();
  851.         if (err) return(err);                            /* Error creating the resource fork. */
  852.         res = HOpenResFile(vrn, pid, fss.name, perm);    /* Now that it exists, open it.  */
  853.         err = ResError();                                /* Return whatever error occurs. */
  854.     }
  855.  
  856.     if (!err) {        /* If no error, then we can use the resource fork. */
  857.         (*frHndl)->fileState.resRefNum = res;
  858.         UseResFile(res);
  859.         err = ResError();
  860.     }
  861.  
  862.     return(err);
  863. }
  864.  
  865.  
  866.  
  867. /*****************************************************************************/
  868.  
  869.  
  870.  
  871. /* If there is a resource fork open for this document, this closes it. */
  872.  
  873. #pragma segment File
  874. OSErr    CloseDocResFile(FileRecHndl frHndl)
  875. {
  876.     short    res;
  877.  
  878.     if ((res = (*frHndl)->fileState.resRefNum) == kInvalRefNum) return(noErr);
  879.         /* If it was never opened, then there's nothing to close. */
  880.  
  881.     if ((*frHndl)->fileState.refNum == res) {
  882.         (*frHndl)->fileState.resRefNum = kInvalRefNum;
  883.         return(noErr);
  884.     }
  885.  
  886.     CloseResFile(res);                                    /* Close the resource fork. */
  887.     (*frHndl)->fileState.resRefNum = kInvalRefNum;        /* Mark it as closed. */
  888.  
  889.     return(ResError());
  890. }
  891.  
  892.  
  893.  
  894. /*****************************************************************************/
  895.  
  896.  
  897.  
  898. #pragma segment File
  899. long    GetModNum(void)
  900. {
  901.     static    modNum = 0;
  902.  
  903.     return(++modNum);
  904. }
  905.  
  906.  
  907.  
  908. /*****************************************************************************/
  909.  
  910.  
  911.  
  912. /* This function returns the state of the document.  If the document
  913. ** is dirty, then true is returned.  If the document is clean, then false
  914. ** is returned. */
  915.  
  916. #pragma segment File
  917. Boolean    GetDocDirty(FileRecHndl frHndl)
  918. {
  919.     if (frHndl) return((*frHndl)->fileState.docDirty);
  920.     return(false);
  921. }
  922.  
  923.  
  924.  
  925. /*****************************************************************************/
  926.  
  927.  
  928.  
  929. /* This function returns the state of the window's document.  If the document
  930. ** is dirty, then true is returned.  If the document is clean, or the window
  931. ** has no document, then false is returned. */
  932.  
  933. #pragma segment File
  934. Boolean    GetWindowDirty(WindowPtr window)
  935. {
  936.     if (IsAppWindow(window)) return(GetDocDirty((FileRecHndl)GetWRefCon(window)));
  937.     return(false);
  938. }
  939.  
  940.  
  941.  
  942. /*****************************************************************************/
  943.  
  944.  
  945.  
  946. #pragma segment File
  947. void    SetDocDirty(FileRecHndl frHndl)
  948. {
  949.     if (frHndl) {
  950.         if (!((*frHndl)->fileState.attributes & kwRuntimeOnlyDoc)) {
  951.             (*frHndl)->fileState.docDirty = true;
  952.             (*frHndl)->fileState.modNum   = GetModNum();
  953.             (*frHndl)->fileState.modTick  = TickCount();
  954.         }
  955.     }
  956. }
  957.  
  958.  
  959.  
  960. /*****************************************************************************/
  961.  
  962.  
  963.  
  964. #pragma segment File
  965. void    SetWindowDirty(WindowPtr window)
  966. {
  967.     if (IsAppWindow(window))
  968.         SetDocDirty((FileRecHndl)GetWRefCon(window));
  969. }
  970.  
  971.  
  972.  
  973. /*****************************************************************************/
  974.  
  975.  
  976.  
  977. /*  The SetDefault function sets the default volume and directory to the volume specified
  978. **  by newVRefNum and the directory specified by newDirID. The current default volume 
  979. **  and directory are returned in oldVRefNum and oldDir and should be used to restore 
  980. **  things to their previous condition *as soon as possible* with the RestoreDefault 
  981. **  function. These two functions are designed to be used as a wrapper around
  982. **  Standard C I/O routines where the location of the file is implied to be the
  983. **  default volume and directory. In other words, this is how you should use these
  984. **  functions:
  985. **
  986. **        err = SetDefault(newVRefNum, newDirID, &oldVRefNum, &oldDirID);
  987. **        if (!err)
  988. **            {
  989. **                -- call the Stdio functions like remove, rename, tmpfile, fopen,   --
  990. **                -- freopen, etc. or non-ANSI extentions like fdopen, fsetfileinfo, --
  991. **                -- create, open, unlink, etc. here!                                   --
  992. **
  993. **                err = RestoreDefault(oldVRefNum, oldDirID);
  994. **            }
  995. **
  996. **  By using these functions as a wrapper, you won't need to open a working directory 
  997. **  (because they use HSetVol) and you won't have to worry about the effects of using
  998. **  HSetVol (documented in Technical Note #140: Why PBHSetVol is Dangerous 
  999. **  and in the Inside Macintosh: Files book in the description of the HSetVol and 
  1000. **  PBHSetVol functions) because the default volume/directory is restored before 
  1001. **  giving up control to code that might be affected by HSetVol.
  1002. ** Use this and the below call instead of the old-style FSpSetWD and FSpResetWD. */
  1003.  
  1004. #pragma segment File
  1005. OSErr    SetDefault(short newVRefNum, long newDirID, short *oldVRefNum, long *oldDirID)
  1006. {
  1007.     OSErr    err;
  1008.  
  1009.     err = HGetVol(nil, oldVRefNum, oldDirID);
  1010.         /* Get the current default volume/directory. */
  1011.  
  1012.     if (!err)
  1013.         err = HSetVol(nil, newVRefNum, newDirID);
  1014.             /* Set the new default volume/directory */
  1015.  
  1016.     return(err);
  1017. }
  1018.  
  1019.  
  1020.  
  1021. /*****************************************************************************/
  1022.  
  1023.  
  1024.  
  1025. #pragma segment File
  1026. OSErr    RestoreDefault(short oldVRefNum, long oldDirID)
  1027. {
  1028.     OSErr    err;
  1029.     short    defaultVRefNum;
  1030.     long    defaultDirID;
  1031.     long    defaultProcID;
  1032.  
  1033.     err = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID);
  1034.         /* Determine if the default volume was a wdRefNum. */
  1035.  
  1036.     if (!err) {
  1037.         /* Restore the old default volume/directory, one way or the other. */
  1038.  
  1039.         if (defaultDirID != fsRtDirID)
  1040.             err = SetVol(nil, oldVRefNum);
  1041.                 /* oldVRefNum was a wdRefNum - use SetVol */
  1042.         else
  1043.             err = HSetVol(nil, oldVRefNum, oldDirID);
  1044.                 /* oldVRefNum was a real vRefNum - use HSetVol */
  1045.     }
  1046.  
  1047.     return(err);
  1048. }
  1049.  
  1050.  
  1051.  
  1052. /*****************************************************************************/
  1053. /*****************************************************************************/
  1054. /*****************************************************************************/
  1055.  
  1056.  
  1057.  
  1058. /* Get the vRefNum and dirID of a file, which is its location. */
  1059.  
  1060. #pragma segment File
  1061. OSErr    GetFileLocation(short refNum, short *vRefNum, long *dirID, StringPtr fileName)
  1062. {
  1063.     FCBPBRec pb;
  1064.     OSErr err;
  1065.  
  1066.     pb.ioNamePtr = fileName;
  1067.     pb.ioVRefNum = 0;
  1068.     pb.ioRefNum  = refNum;
  1069.     pb.ioFCBIndx = 0;
  1070.  
  1071.     err = PBGetFCBInfoSync(&pb);
  1072.  
  1073.     if (vRefNum)
  1074.         *vRefNum = pb.ioFCBVRefNum;
  1075.     if (dirID)
  1076.         *dirID = pb.ioFCBParID;
  1077.  
  1078.     return(err);
  1079. }
  1080.  
  1081.  
  1082.  
  1083. /*****************************************************************************/
  1084.  
  1085.  
  1086.  
  1087. /* After getting a resource, you can't actually be sure that it came from the current
  1088. ** resource file.  Even if you make a call such as Get1Resource, starting with system 7.1,
  1089. ** you can't really be sure that it came from the current resource file.  (The resource
  1090. ** files may be overridden, or they may be flagged to be extended, as is the case with
  1091. ** font files.)  This checks to see that the resource actually came from the current
  1092. ** resource file.  If it didn't, then the handle returned is nil, and the error returned
  1093. ** is resNotFound.  (You probably don't need this function unless you are doing some kind
  1094. ** of resource-editing function.) */
  1095.  
  1096. #pragma segment File
  1097. OSErr    CurResOnly(Handle *hndl)
  1098. {
  1099.     short    cr, hr;
  1100.     OSErr    err;
  1101.  
  1102.     cr  = CurResFile();
  1103.     hr  = HomeResFile(*hndl);
  1104.     err = ResError();
  1105.     if (hr == -1) err = resNotFound;
  1106.     if (!hr)      hr  = 2;                /* Home res file is the system file. */
  1107.  
  1108.     if (cr != hr) *hndl = nil;
  1109.     return(err);
  1110. }
  1111.  
  1112.  
  1113.  
  1114.